package jp.ne.motoki.android.ml14971;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.util.Log;

/**
 * バックグラウンドで動作するタスクを表すクラス。
 * ただし、このクラスはタスクの実行・キャンセルをサポートするだけで、実際の処理は内部クラスに委譲している。
 * 処理をキャンセルするためには、スレッドにインタラプトすること。
 * @author kiyoshi.motoki
 *
 */
public class BackGroundTask extends FutureTask<Void> {
    
    public BackGroundTask(Context context, Handler target) {
        super(new Worker(context, target));
    }

    /**
     * バックグラウンドで実行させたい処理を表すクラス。
     * @author kiyoshi.motoki
     *
     */
    private static class Worker implements Callable<Void> {
        
        private static final int INIT = 5;
        private int counter = INIT;
        
        private final Context context;
        private final Handler target;
        
        // UIスレッドと、バックグラウンドスレッドの双方からアクセスされる変数。
        // 複数スレッドからアクセスされる変数を扱うコードは、同期化（synchronized）する必要がある。
        private boolean isSet = false;
        private int parameter = 0;
        
        public Worker(Context context, Handler target) {
            if (context == null) {
                throw new NullPointerException("context == null");
            }
            if (target == null) {
                throw new NullPointerException("target == null");
            }
            this.context = context;
            this.target = target;
        }

        /**
         * バックグラウンドで動作する処理。
         * このタスクが動作しているスレッドにインタラプトすることで、処理をキャンセルすることができる。
         */
        @Override
        public Void call() {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    counter--;
                    Log.d("test", "worker running : counter = " + counter);
                    
                    if (counter == 0) {
                        counter = INIT;
                        String emotion = blockAndGetEmotion();
                        Log.d("test", "I'm " + emotion);
                    }
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                // Do nothing
            }
            Log.w("test", "task canceled");
            return null;
        }
        
        /**
         * あなたの気分をたずねる。
         * あなたが返答するまで、スレッドはブロックされる。
         * 
         * @return あなたの気分
         */
        private String blockAndGetEmotion() {
            
            target.post(POST_RUNNABLE);
            
            int parameter = 0;

            // wait()を使用するコードの標準形。
            // wait()は勝手に起きることがあるので、必ずループの中で呼び出す。
            synchronized (this) {
                while (!isSet()) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        Log.e("test", "wait interrupted", e);
                    }
                }
                parameter = getAndResetParameter();
            }
            
            return parameter == DialogInterface.BUTTON_POSITIVE ? "Positive!" : "Negative..";
        }
        
        /**
         * パラメタをセットして、wait()しているスレッドを起動する。
         * @param parameter
         */
        private synchronized void setParameter(int parameter) {
            this.parameter = parameter;
            isSet = true;
            notifyAll();
        }
        
        private synchronized boolean isSet() {
            return isSet;
        }
        
        /**
         * パラメタを取得し、かつ値をリセットする。
         * @return
         */
        private synchronized int getAndResetParameter() {
            int parameter = this.parameter;
            
            this.parameter = 0;
            isSet = false;
            
            return parameter;
        }
        
        private final DialogInterface.OnClickListener LISTENER =
                new DialogInterface.OnClickListener() {
            
            @Override
            public void onClick(DialogInterface dialog, int which) {
                setParameter(which);
            }
        };
        
        private final Runnable POST_RUNNABLE = new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setTitle("You are..")
                    .setPositiveButton("Positive!", LISTENER)
                    .setNegativeButton("Negative..", LISTENER)
                    .setCancelable(false)
                    .show();
            }
        };
    }
}
